/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include <map>
#include <thread>
#include <fstream>
#include <iostream>
#include <functional>
#include "BackintConst.h"
#include "common/File.h"
#include "RestoreHandler.h"

using namespace backint;


namespace {
    const std::string RESTORE_SUCCESS = "#RESTORED";
    const std::string RESTORE_ERROR = "#ERROR";
    const std::string VERSION_1_0 = "1.0";
    const int READ_WRITE_DATA_BUFFER = 4096;
    using Defer = std::shared_ptr<void>;
}

RestoreHandler::RestoreHandler()
{
}

uint32_t RestoreHandler::ExecRestore(const StuParam &inParam, const PlugParams &plugParam)
{
    GetRestoreParam(inParam, plugParam);
    StuFileInfo stFileInfo;
    if (ReadPipeInfo(inParam.m_inputFile, stFileInfo) != SUCCESS) {
        ERRLOG("Failed to read pipe info.");
        return FAILED;
    }
    if (stFileInfo.m_eBIDData.empty() && stFileInfo.fileData.empty()) {
        ERRLOG("Pipe info and file info is empty.");
        return FAILED;
    }
    return DataRestore(inParam, stFileInfo);
}

uint32_t RestoreHandler::DataRestore(const StuParam &inParam, const StuFileInfo &stFileInfo)
{
    std::vector<std::string> outFileTitle = {stFileInfo.m_inputFileTitle};
    if (WriteOutFile(outFileTitle, m_outputFile) != SUCCESS) {
        ERRLOG("Failed to write output file.");
        return FAILED;
    }
    INFOLOG("%d.", stFileInfo.m_eBIDData.size());
    if (stFileInfo.m_eBIDData.size() == 0) {
        ReadWriteData(inParam, stFileInfo, "catalog");
    } else {
        ReadWriteData(inParam, stFileInfo, "data");
    }
    return RestoreResult();
}

uint32_t RestoreHandler::RestoreResult()
{
    m_outputValue["pid"] = m_pid;
    m_outputValue["job_id"] = m_jobId;
    m_outputValue["sub_jod_id"] = m_subJobId;
    m_outputValue["code"] = m_restoreResult?SUCCESS:FAILED;
    WriteResultToFile(m_outputValue, m_resultFile);
    return m_restoreResult?SUCCESS:FAILED;
}

void RestoreHandler::GetRestoreParam(const StuParam &inParam, const PlugParams &plugParam)
{
    m_pid = plugParam.m_pid;
    m_dbName = plugParam.m_dbName;
    m_jobId = plugParam.m_jobId;
    m_subJobId = plugParam.m_subJobId;
    m_cachePath = plugParam.m_cachePath;
    m_dataPath = plugParam.m_dataPath;
    m_outputFile = inParam.m_outFile;
    m_backupID = inParam.m_dtBackupId;
    m_resultFile = m_cachePath + "/" + "result" + "_" + m_dbName + "_" + "recovery" + "_" + m_pid + ".txt";
}

void RestoreHandler::ReadWriteData(const StuParam &inParam, const StuFileInfo &fileInfo, const std::string type)
{
    for (const auto &file : fileInfo.fileData) {
        std::string outputFile = GetFileName(file.m_filePath);
        std::string inputFile = m_dataPath + "/" + outputFile;
        std::thread readFileThreads(&RestoreHandler::WriteDataToFile, this, inputFile, file.m_filePath);
        readFileThreads.detach();
        m_threadCount++;
    }
    for (const auto &bidFile : fileInfo.m_eBIDData) {
        std::string bidOutputFile = GetFileName(bidFile.m_filePath);
        std::string bidInputFile = m_dataPath + "/" + bidOutputFile;
        std::thread readFileThreads(&RestoreHandler::WriteDataToFile, this, bidInputFile, bidFile.m_filePath);
        readFileThreads.detach();
        m_threadCount++;
    }
    while (true) {
        if (m_threadCount == 0) {
            INFOLOG("All read write data thread exit, will return.");
            return;
        }
    }
}

uint32_t RestoreHandler::WriteDataToFile(const std::string &inputFile, const std::string &outputFile)
{
    Defer _(nullptr, [&](...) {
        std::lock_guard<std::mutex> lock(m_writeOutFileMutex);
        WriteOutFile(m_outputResult, m_outputFile);
    });
    std::ifstream pipe(inputFile, std::ios::binary | std::ios::in);
    std::ofstream output(outputFile, std::ios::binary | std::ios::out);
    if (!pipe.is_open()) {
        m_threadCount--;
        m_restoreResult = false;
        m_outputResult = {RESTORE_ERROR, inputFile};
        ERRLOG("Failed to open pipe file: %s.", inputFile.c_str());
        return FAILED;
    }

    if (!output.is_open()) {
        m_threadCount--;
        m_restoreResult = false;
        m_outputResult = {RESTORE_ERROR, inputFile};
        pipe.close();
        ERRLOG("Failed to open output file: %s.", outputFile.c_str());
        return FAILED;
    }
    std::string line;
    int size = 0;
    char readBuffer[READ_WRITE_DATA_BUFFER] = {0};
    while (pipe.read(readBuffer, READ_WRITE_DATA_BUFFER)) {
        size += pipe.gcount();
        output.write(readBuffer, READ_WRITE_DATA_BUFFER);
    }
    pipe.close();
    output.close();
    m_threadCount--;
    m_outputResult = {RESTORE_SUCCESS, m_backupID, inputFile, std::to_string(size)};
    return SUCCESS;
}

std::string RestoreHandler::GetFileName(const std::string &filePath)
{
    size_t pos = filePath.find_last_of('/');
    if (pos == std::string::npos) {
        return filePath;
    }
    return filePath.substr(pos + 1);
}